#!/usr/bin/env python2

from eagle import *

class Undo(object):
    def __init__(self, app):
        self.last_images = []
        self.app = app
        app.undo = self
        self.canvas = app["canvas"]
        self.button = app["undo"]
        self.button.set_inactive()


    def push(self):
        img = self.canvas.get_image()
        self.last_images.append(img)
        self.button.set_active()


    def pop(self):
        if self.last_images:
            img = self.last_images.pop()
            self.canvas.draw_image(img)

        if not self.last_images:
            self.button.set_inactive()



class Tool(object):
    """Interface to be implemented by tools."""

    def set_active(self, app):
        """This tool is now active."""
        pass


    def set_inactive(self, app):
        """This tool is now inactive. """
        pass


    def mouse(self, app, canvas, buttons, x, y):
        """This tool have a user feedback using mouse on canvas."""
        pass



class Line(Tool):
    def __init__(self):
        self.first_point = None
        self.message_id = None


    def set_active(self, app):
        self.message_id = app.status_message(
            "Press the left mouse button to mark the first point.")


    def set_inactive(self, app):
        if self.message_id is not None:
            app.remove_status_message(self.message_id)


    def mouse(self, app, canvas, buttons, x, y):
        if buttons & Canvas.MOUSE_BUTTON_1:
            if self.first_point is None:
                self.first_point = (x, y)
                self.inner_message_id = app.status_message(
                    ("First poit at (%d, %d). Now mark the second.") %
                    (x, y))
            else:
                color = app["fg"]
                size  = app["size"]
                x0, y0 = self.first_point
                app.undo.push()
                canvas.draw_line(x0, y0, x, y, color, size)
                self.first_point = None
                app.remove_status_message(self.inner_message_id)



class Pencil(Tool):
    def __init__(self):
        self.last_point = None
        self.message_id = None
        self.changed = False


    def set_active(self, app):
        self.message_id = app.status_message(
            "Press the left mouse button and move your mouse.")


    def set_inactive(self, app):
        if self.message_id is not None:
            app.remove_status_message(self.message_id)


    def mouse(self, app, canvas, buttons, x, y):
        if buttons & Canvas.MOUSE_BUTTON_1:
            if not self.changed:
                app.undo.push()
            self.changed = True

            color = app["fg"]
            size  = app["size"]
            if self.last_point is not None:
                x0, y0 = self.last_point
            else:
                x0 = x + 1
                y0 = y

            if size == 1:
                canvas.draw_point(x, y, color)
            else:
                half = size / 2
                canvas.draw_rectangle(x-half, y-half, size, size, color, 1,
                                      color, True)
            canvas.draw_line(x0, y0, x, y, color, size)
            self.last_point = (x, y)
        else:
            # Button 1 was released, reset last point
            self.last_point = None
            self.changed = False



class Rectangle(Tool):
    def __init__(self):
        self.first_point = None
        self.message_id = None


    def set_active(self, app):
        app["rectgroup"].show()
        self.message_id = app.status_message(
            "Press the left mouse button to mark first point.")


    def set_inactive(self, app):
        app["rectgroup"].hide()
        if self.message_id is not None:
            app.remove_status_message(self.message_id)


    def mouse(self, app, canvas, buttons, x, y):
        if buttons & Canvas.MOUSE_BUTTON_1:
            if self.first_point is None:
                self.first_point = (x, y)
                self.inner_message_id = app.status_message(
                    ("First poit at (%d, %d). Now mark the second.") %
                    (x, y))
            else:
                fg   = app["fg"]
                bg   = app["bg"]
                size = app["size"]
                fill = app["rectfill"]

                x0, y0 = self.first_point

                if x0 > x:
                    x0, x = x, x0
                if y0 > y:
                    y0, y = y, y0

                w = x - x0
                h = y - y0

                app.undo.push()
                canvas.draw_rectangle(x0, y0, w, h, fg, size, bg, fill)
                self.first_point = None
                app.remove_status_message(self.inner_message_id)



class Text(Tool):
    def __init__(self):
        self.message_id = None


    def set_active(self, app):
        app["textgroup"].show()
        self.message_id = app.status_message(
            "Type your text in 'Contents' and press the left button " \
                "to place it.")


    def set_inactive(self, app):
        app["textgroup"].hide()
        if self.message_id is not None:
            app.remove_status_message(self.message_id)


    def mouse(self, app, canvas, buttons, x, y):
        if buttons & Canvas.MOUSE_BUTTON_1 and app["text"]:
            text  = app["text"]
            fg    = app["fg"]
            bg    = app["bg"]
            font  = app["font"]

            if app["textbgtransp"]:
                bg = None

            app.undo.push()
            canvas.draw_text(text, x, y, fg, bg, font)



tools = {
    "Line": Line(),
    "Pencil": Pencil(),
    "Rectangle": Rectangle(),
    "Text": Text(),
    }
def_tool="Line"


def tool_changed(app, tool, value):
    if tool_changed.last_tool:
        tool_changed.last_tool.set_inactive(app)

    t = tools[value]
    tool_changed.last_tool = t
    t.set_active(app)
tool_changed.last_tool = None


def canvas_action(app, canvas, buttons, x, y):
    tool = app["tool"]
    tools[tool].mouse(app, canvas, buttons, x, y)


def save(app, button, filename):
    canvas = app["canvas"]
    img = canvas.get_image()
    try:
        img.save(filename)
    except Exception, e:
        error(str(e))


def clear(app, button):
    app.undo.push()
    app["canvas"].clear()


def confirm_quit(app):
    return yesno("Are you sure you want to close '%s'?" % app.title)


def do_undo(app, button):
    app.undo.pop()



app = App(title="Paint",
          id="paint",
          statusbar=True,
          quit_callback=confirm_quit,
          left=(Color(id="fg",
                      label="Foreground:",
                      color="black",
                      ),
                Color(id="bg",
                      label="Background:",
                      color=(255, 0, 0),
                      ),
                Selection(id="tool",
                          label="Tool:",
                          options=tools.keys(),
                          value=def_tool,
                          callback=tool_changed,
                          ),
                UIntSpin(id="size",
                         label="Line Size:",
                         min=1,
                         value=1,
                         ),
                ),
          right=(Group(id="textgroup",
                       label="Text Properties:",
                       children=(Entry(id="text",
                                       label="Contents:",
                                       ),
                                 Font(id="font",
                                      label="Font:",
                                      ),
                                 CheckBox(id="textbgtransp",
                                          label="Transparent background?",
                                          ),
                                 ),
                       ),
                 Group(id="rectgroup",
                       label="Rectangle Properties:",
                       children=(CheckBox(id="rectfill",
                                          label="Fill?",
                                          ),
                                 ),
                       ),
                 ),
          top=(SaveFileButton(callback=save),
               CloseButton(),
               Button(id="undo",
                      stock="undo",
                      callback=do_undo,
                      ),
               Button(id="clear",
                      stock="clear",
                      callback=clear,
                      ),
               ),
          center=(Canvas(id="canvas",
                         label="Draw Here:",
                         width=400,
                         height=400,
                         bgcolor="white",
                         callback=canvas_action,
                         ),
                  )
          )

for tool in tools.itervalues():
    tool.set_inactive(app)

tool_changed(app, app.get_widget_by_id("tool"), def_tool)
Undo(app)

run()
